from khronos.utils import Namespace, INF

class Shape(object):
    @staticmethod
    def erase(self, canvas, ids):
        """'ids' should be a canvas object id, or a list of ids, or lists, basically what is 
        returned by a shape's draw() method."""
        if isinstance(ids, list):
            for id in ids:
                Shape.erase(canvas, id)
        else:
            canvas.delete(id)
            
class Text(Shape):
    """(x, y) represents the text's center position."""
    def __init__(self, text="", x=0, y=0, **props):
        self.text = text
        self.x = x
        self.y = y
        self.props = Namespace(props)
        
    def move(self, dx=0, dy=0):
        self.x += dx
        self.y += dy
        
    def scale(self, xscale=1, yscale=1):
        self.x *= xscale
        self.y *= yscale
        
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        return canvas.create_text(x + self.x,  -(y + self.y), text=self.text, **final_props)
        
    def copy(self):
        return self.__class__(self.text, self.x, self.y, **self.props)
        
class Rectangle(Shape):
    """(x, y) represents the rectangle's center position."""
    def __init__(self, width=50, height=50, x=0, y=0, **props):
        self.width = width
        self.height = height
        self.x = x
        self.y = y
        self.props = Namespace(props)
        
    def move(self, dx=0, dy=0):
        self.x += dx
        self.y += dx
        
    def scale(self, xscale=1, yscale=1):
        self.x *= xscale
        self.y *= yscale
        self.width *= xscale
        self.height *= yscale
        
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        left   = x + self.x - self.width / 2.0
        right  = left + self.width
        top    = -(y + self.y + self.height / 2.0)
        bottom = top + self.height
        return canvas.create_rectangle(left, top, right, bottom, **final_props)
        
    def copy(self):
        return self.__class__(self.width, self.height, self.x, self.y, **self.props)
        
class Oval(Rectangle):
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        left   = x + self.x - self.width / 2.0
        right  = left + self.width
        top    = -(y + self.y + self.height / 2.0)
        bottom = top + self.height
        return canvas.create_oval(left, top, right, bottom, **final_props)
        
class Triangle(Rectangle):
    def __init__(self, width=50, height=50, spike=0.5, x=0, y=0, **props):
        Rectangle.__init__(self, width, height, x, y, **props)
        self.spike = spike
        
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        left   = x + self.x - self.width / 2.0
        right  = left + self.width
        top    = -(y + self.y + self.height / 2.0)
        bottom = top + self.height
        spike  = left + (right - left) * self.spike
        return canvas.create_polygon(left,  bottom, spike, top, right, bottom, **final_props)
        
    def copy(self):
        return self.__class__(self.width, self.height, self.spike, self.x, self.y, **self.props)
        
class Line(list, Shape):
    """List of (x, y) tuples."""
    def __init__(self, *args, **props):
        list.__init__(self, *args)
        self.props = Namespace(props)
        
    def move(self, dx=0, dy=0):
        for i in xrange(len(self)):
            px, py = self[i]
            self[i] = (px + dx, py + dy)
            
    def scale(self, xscale=1, yscale=1):
        for i in xrange(len(self)):
            px, py = self[i]
            self[i] = (px * xscale, py * yscale)
            
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        points = [(x + px, -(y + py)) for (px, py) in self]
        return canvas.create_line(*points, **final_props)
        
    def copy(self):
        return self.__class__(self, **self.props)
        
class Polygon(Line):
    """List of (x, y) tuples. Last point connects to the first."""
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        points = [(x + px, -(y + py)) for (px, py) in self]
        return canvas.create_polygon(*points, **final_props)
        
class ShapeList(list, Shape):
    """List of shape objects (may include other shape lists inside). First objects are drawn below 
    the last. Additionally to refer to items by index, the ShapeList class allows binding names to
    indices, and latter accessing these positions by their name rather than by index."""
    def __init__(self, *args, **props):
        list.__init__(self, *args)
        self.binding = {}
        self.props = Namespace(props)
        
    def __getitem__(self, x):
        if isinstance(x, int):
            return list.__getitem__(self, x)
        return list.__getitem__(self, self.binding[x])
        
    def bind(self, **bindings):
        for name, index in bindings.iteritems():
            self.binding[name] = index
            
    def move(self, dx=0, dy=0):
        for shape in self:
            shape.move(dx, dy)
            
    def scale(self, xscale=1, yscale=1):
        for shape in self:
            shape.scale(xscale, yscale)
            
    def draw(self, canvas, x, y, **props):
        final_props = self.props
        if len(props) > 0:
            final_props = dict(self.props)
            final_props.update(props)
        return [shape.draw(canvas, x, y, **final_props) for shape in self]
        
    def copy(self):
        shapelist = self.__class__([shape.copy() for shape in self], **self.props)
        shapelist.binding = dict(self.binding)
        return shapelist
        
Shape.text = Text
Shape.rect = Rectangle
Shape.oval = Oval
Shape.tri  = Triangle
Shape.line = Line
Shape.poly = Polygon
Shape.list = ShapeList
